package oop.controller;

import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import oop.conf.ActionDescriptor;
import oop.conf.Config;
import oop.conf.ModuleDescriptor;
import oop.controller.action.Action;
import oop.controller.module.Module;
import oop.data.User;
import oop.util.HibernateUtil;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;

/**
 * Servlet implementation class Test
 */
public class ControllerServlet extends HttpServlet {
	
	private static final long serialVersionUID = 1L;

	public ControllerServlet() {
	}

	public void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		process(request, response);
	}

	@Override
	public void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		process(request, response);
	}

	protected void process(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		// set encoding
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");

		// set variables
		String template = StringUtils.defaultIfEmpty((String) request
				.getSession().getAttribute("template"), "default");
		String templatePath = Config.get().getHomeDir() + "/templates/" + template;
		String templateEntry = "/templates/" + template + "/index.jsp";

		request.setAttribute("config", Config.get());
		request.setAttribute("homeDir", Config.get().getHomeDir());
		request.setAttribute("scriptPath", getScriptPath());
		request.setAttribute("templatePath", templatePath);
		request.setAttribute("pageTitle", Config.get().getSiteName());

		// perform action
		try {
			// get action descriptor
			String actionStr;
			if (request.getServletPath().startsWith(Config.get().getActionPath())) {
				actionStr = request.getPathInfo().substring(1);
			} else {
				actionStr = StringUtils.defaultIfEmpty(request
						.getParameter("action"), "homepage");
			}
			ActionDescriptor actionDesc = Config.get().getActionDescriptor(
					actionStr);
			if (actionDesc == null) {
				if (actionDesc == null) {
					error(request, response, "Không tìm thấy hành động: "
							+ actionStr);
					return;
				}
			}
			
			// check permission
			if (actionDesc.isLoginRequired() && !isUserLoggedIn(request)) {
				error(request, response,
						"Bạn cần đăng nhập để thực hiện chức năng này.");
				return;
			}
			String requiredGroup = actionDesc.getRequiredGroup();
			User user = getUser(request);
			if (requiredGroup != null
					&& (user == null || !requiredGroup.equals(user.getGroup()))) {
				error(request, response, "Bạn cần phải thuộc nhóm "
						+ requiredGroup + " để thực hiện chức năng này.");
				return;
			}

			// perform
			Action action = actionDesc.getActionClass().newInstance();
			action.setController(this);
			action.setRequest(request);
			action.setResponse(response);
			action.perform();

			// forward next action or jsp
			request.getSession().setAttribute("previousQuery",
					request.getSession().getAttribute("currentQuery"));
			if (!(StringUtils.isEmpty(request.getQueryString())
					|| "error".equals(actionStr)
					|| "user.login".equals(actionStr) || "user.logout"
					.equals(actionStr))) {
				request.getSession().setAttribute("currentQuery",
						request.getQueryString());
			}
			if (!action.isPreventForward()) {
				if (action.getNextAction() == null) {
					request.setAttribute("modules", getModules(request));
					request.setAttribute("action", action);

					String uri = templateEntry + "?action=" + actionStr;
					request.getRequestDispatcher(uri)
							.forward(request, response);
				} else {
					String uri = getMainEntry() + "?action="
							+ action.getNextAction();
					request.getRequestDispatcher(uri)
							.forward(request, response);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			error(request, response, e.toString());
		} finally {
			HibernateUtil.closeSession();
		}
	}
	
	private void error(HttpServletRequest request, HttpServletResponse response, String message) throws ServletException, IOException {
		request.setAttribute("message", message);
		String uri = getMainEntry() + "?action=error";
		request.getRequestDispatcher(uri).forward(request, response);
	}

	private Map<String, List<Module>> getModules(HttpServletRequest request) {
		Map<String, List<Module>> moduleMap = new HashMap<String, List<Module>>();
		boolean logedIn = isUserLoggedIn(request);
		User user = getUser(request);
		for (ModuleDescriptor descriptor : Config.get().getModuleDescriptors()) {
			if (descriptor.isLoginRequired() && !logedIn) {
				continue;
			}
			if (descriptor.getRequiredGroup() != null
					&& !(logedIn && user.getGroup().equals(
							descriptor.getRequiredGroup()))) {
				continue;
			}
			try {
				Module module = descriptor.getModuleClass().newInstance();
				module.setTitle(descriptor.getTitle());
				module.setPage(descriptor.getPage());
				module.setOrder(descriptor.getOrder());
				module.init();
				if (moduleMap.get(descriptor.getPosition()) == null) {
					moduleMap.put(descriptor.getPosition(),
							new LinkedList<Module>());
				}
				moduleMap.get(descriptor.getPosition()).add(module);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		// sắp xếp theo thứ tự
		for (List<Module> modules : moduleMap.values()) {
			Collections.sort(modules, moduleComparator);
		}
		return moduleMap;
	}
	
	protected boolean isUserLoggedIn(HttpServletRequest request) {
		return (Boolean) (ObjectUtils.defaultIfNull(request.getSession()
				.getAttribute("login"), Boolean.FALSE));
	}

	protected User getUser(HttpServletRequest request) {
		return (User) request.getSession().getAttribute("user");
	}

	public String getMainEntry() {
		return Config.get().getMainEntry();
	}
	
	public String getScriptPath() {
		return Config.get().getHomeDir() + getMainEntry();
	}

	private static Comparator<Module> moduleComparator = new Comparator<Module>() {
		
		@Override
		public int compare(Module o1, Module o2) {
			return o1.getOrder() - o2.getOrder();
		}
	};
	
}
